
你是タコたち嗎?你喜歡吃cookies嗎?那麼你知道cookies是怎麼被製造出來的嗎?
cookies是在瀏覽器儲存的小小資料片段,通常來說當瀏覽器發出request時,有可能同時將cookie發送出去。利用這個特性,可以將通常來說無狀態的HTTP保有記憶,做到登入功能、追蹤行爲等等。
雖然我們可以透過瀏覽器開發工具新增cookie。

但通常這是由Web Server製造,然後透過Response Headers發送給瀏覽器。
這次同樣使用FastAPI建立Web Server提供服務。首先先安裝一下相關套件:
pip install fastapi uvicorn
然後建立index.html檔案,利用JavaScript將cookie顯式在畫面上:
<!-- index.html -->
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <title>takodachi cookies</title>
  </head>
  <body>
    <img alt="" src="https://i.ytimg.com/vi/me4Es5Yqhxk/maxresdefault.jpg"/>
    <textarea cols="30" id="display_cookie" name="" rows="10"></textarea>
    <script type="text/javascript">
     window.addEventListener('load', () => {
         console.log(document.cookie);
         let textarea = document.querySelector('#display_cookie');
         textarea.value = document.cookie;
     });
    </script>
  </body>
</html>
然後同樣利用FileResponse將index.html提供給瀏覽器。建立app.py檔案並輸入以下內容
# app.py
from fastapi import FastAPI
from fastapi.responses import FileResponse, Response
app = FastAPI()
@app.get('/index.html')
def index():
    headers = {
        'Set-Cookie': 'name=takodachi;',
    }
    return FileResponse('index.html', headers=headers)
不同的是:需要添加一個自定義的Header,格式爲:Set-Cookie: <key>=<value>,比如:Set-Cookie: hello=world。不過<key>和<value>還是有一些限制在,有一些字符是具有特殊意義,所以有可能將<value>使用百分號編碼方式處理。
啓動服務器
uvicorn app:app
然後瀏覽 http://localhost:8000/index.html


你可能不會立馬在畫面上看到結果。可以重新整理畫面看看。

才一個cookie根本不夠,我需要更多、更多、我要更多的cookies。
如果需要多個cookies,那就需要建立多個Set-Cookie的Headers。上面的寫法就無法滿足要求,需要調整一下寫法。
@app.get('/index1.html')
def index1():
    response = FileResponse('index.html')
    response.set_cookie('name', 'takodachi')
    response.set_cookie('like', 'cookie')
    return response
這次得用FileResponse的set_cookie方法製造cookies。然後瀏覽 http://localhost:8000/index1.html


就像之前所說,瀏覽器會把cookie跟著request發送出去。這次多添加一個endpoint並不製造任何cookies:
@app.get('/index0.html')
def index0():
    return FileResponse('index.html')
瀏覽 http://localhost:8000/index0.html



如果沒有寫上有效期限,就意味者這個cookie是個 即期食品 。當瀏覽器關閉重開後也就過期了。

警告: 然而,很多 Web 浏览器支持会话恢复功能,这个功能可以使浏览器保留所有的 tab 标签,然后在重新打开浏览器的时候将其还原。与此同时,cookie 也会恢复,就跟从来没有关闭浏览器一样。^1
如果要設定過期時間,可以在Response Header多添加Expires的資訊,通常有效期限的格式是GMT(%a, %d %b %Y %H:%M:%S GMT)。那麼整個會變成Set-Cookie: <key>=<value>;expires=<有效期限>,比如:2day=xyz; expires=Tue, 27 Sep 2022 11:17:23 GMT;。
在FastAPI裡,可以設定過期的秒數,框架會轉換爲GMT格式。
@app.get('/index2.html')
def index2():
    response = FileResponse('index.html')
    expires = 172800  # 2 day * 24 hour * 60 min * 60 sec
    response.set_cookie('2day', 'xyz', expires=expires)
    return response


或者可以設置Max-Age指定幾妙後過期。那麼整個會變成Set-Cookie: <key>=<value>;Max-Age=<有效秒數>,比如:1day=xyz; Max-Age=86400;。
@app.get('/index3.html')
def index3():
    response = FileResponse('index.html')
    max_age = 86400  # 1 day * 24 hour * 60 min * 60 sec
    response.set_cookie('1day', 'xyz', max_age=max_age)
    return response

如果同時設定了
Expires和Max-Age。那麼Max-Age有更高的優先圈。
Set-Cookie並沒有刪除cookie的操作,不過透過設定有效期限Expires或Max-Age,就可以讓瀏覽器看到:「喔~這過期不能吃了」,然後把它丟掉。

所以可以透過Expires設定一個過去時間。比如1970年的1月1日。
@app.get('/index2-1.html')
def index2_1():
    response = FileResponse('index.html')
    response.set_cookie('2day', 'xyz', expires='Thu, 01 Jan 1970 00:00:00 GMT')
    return response
這裏我是故意寫成字串的。
但依照FastAPI框架行爲,你可以直接設置爲負數的整數值,便會是過期的時間。
或是將Max-Age設定爲0或-1。
@app.get('/index3-1.html')
def index3_1():
    response = FileResponse('index.html')
    max_age = -1  # 1 day * 24 hour * 60 min * 60 sec
    response.set_cookie('1day', 'xyz', max_age=max_age)
    return response
這麼一來,在瀏覽 http://localhost:8000/index2-1.html 和 http://localhost:8000/index3-1.html 後,瀏覽器就會分別把2day和1day的cookies丟棄。
本文同時發表於我的隨筆